﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Brainee.NeuralNetwork
{
  public class Layer : IEnumerable<Neuron>
  {
    #region Protected fields

    protected Neuron[] _neurons;
    private Layer _previous;
    private Layer _next;
    protected IActivationFunction _tf;

    #endregion
    #region Public properties

    public Neuron this [int index] { get { return _neurons [index]; } }

    public int OutputCount { get { return _neurons.Length; } }
    public int InputCount { get { return (_previous != null )? _previous.OutputCount : OutputCount; } }

    public Layer Previous
    {
      get { return _previous; }
      set
      {
        if (_previous != null)
          throw new InvalidOperationException ("Previous layer cannot be modified once it have been set");

        _previous = value;
        _previous._next = this;

        for (int i = 0; i < _neurons.Length; i++)
        {
          _neurons[i] = new Neuron (_tf, _previous.OutputCount);
        }
      }
    }

    public Layer Next
    {
      get { return _next; }
      set
      {
        if (_next != null)
          throw new InvalidOperationException ("Next layer cannot be modified once it have been set");

        _next = value;
      }
    }

    #endregion

    public Layer (IActivationFunction tf, int outputs)
    {
      _neurons = new Neuron[outputs];
      _tf = tf;
      _previous = null;
      _next = null;
    }

    public void Randomize ()
    {
      foreach (Neuron n in _neurons)
      {
        n.Randomize ();
      }
    }

    public double [] Compute (double [] stim)
    {
      double[] result = new double [_neurons.Length];

      for (int i = 0; i < _neurons.Length; i++)
      {
        result [i] = _neurons [i].Compute (stim);
      }

      return result;
    }

    #region IEnumerable<Neuron> Members

    public IEnumerator<Neuron> GetEnumerator ()
    {
      return _neurons.AsEnumerable ().GetEnumerator ();
    }

    #endregion
    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator ()
    {
      return _neurons.GetEnumerator ();
    }

    #endregion
  }

  public class InputLayer : Layer
  {
    public InputLayer (int size)
      : base(null, size)
    {
      for (int i = 0; i < _neurons.Length; i++)
      {
        _neurons [i] = new InputNeuron (i);
      }
    }
  }
}
